﻿#include <stdio.h>
#include <Windows.h>
#include<cstdlib>
#include "IniFile.h"
#include <c2d2/chien2d2.h>
#include <c2d2/chien2d2mapa.h>
#include <c2d2/chien2d2primitivas.h>
#include <c2d2/chienaudio2.h>
#include <c2d2/ator.h>

#include <c2d2/chien2d2lua.h>
#include <c2d2/atorlua.h>

#include "jogo_atores.h"
#include "hero.h"
#include "enemy.h"
#include "jogo_mira.h"
#include "game_helper.h"

// As dimensões da tela desejada (cheia)
#define LARGURA_TELA	640
#define ALTURA_TELA		480
// As dimensões da janela na qual vamos desenhar o jogo
#define LARGURA_JANELA	580
#define ALTURA_JANELA	400
// Quantos pontos devemos ignorar no eixo x,y para colocar a janela no centro
#define OFFX			(LARGURA_TELA-LARGURA_JANELA)/2
#define OFFY			(ALTURA_TELA-ALTURA_JANELA)/2



//variaveis globais :\~ pffffff
unsigned int arial32=0;
unsigned int fonte32=0;
unsigned int fonte64=0;
unsigned int botao_jogar=0;
unsigned int botao_creditos=0;
unsigned int botao_voltar=0;
unsigned int botao_sair=0;
unsigned int botao_load=0;
unsigned int titulo=0;
unsigned int mcredito=0;
unsigned int mapa=0;
unsigned int spmira=0;
// Indica se deve encerrar ou não a aplicação
bool encerra=false;
bool load = false;
// Indica o estado atual (tela) do jogo
// Valores:
//      1 - Menu principal
//      2 - Jogo
//      3 - Créditos
int estadoatual=1;
Hero *hero;
// Enemy *enemy;
Enemy **inimigos;
int numInimigos=0;
Ator *mira;



void telaJogo(){
	C2D2_Mouse *mouse = C2D2_PegaMouse();
	bool cMira = JOGO_CarregaMira();
	// Testa se carregou certo (se é diferente de 0)
	if(!cMira || fonte32==0 || mapa == 0)
	{
		printf("Falhou ao carregar alguma coisa. Encerrando.\n");
		C2D2M_Encerra();
		C2D2_Encerra();
		CL2_Encerra();
	}
	// Dá a velocidade para a camada do mapa
	C2D2M_VelocidadeCamadaMapa(mapa,0,1);
	// Qual a camada de marcas
	C2D2M_CamadaMarcas(mapa,1,208);
	// Recupera o teclado
	C2D2_Botao *teclas = C2D2_PegaTeclas();
	// Coloca a cor de limpeza de tela
	C2D2_TrocaCorLimpezaTela(16,16,16);
	// Procura no mapa a posição para iniciar a personagem
	int xinicial, yinicial;
	if (load) {
		xinicial = atoi(CIniFile::GetValue("HERO_INIT_X","Main", GameHelper::saveFile).c_str());
		yinicial = atoi(CIniFile::GetValue("HERO_INIT_Y","Main", GameHelper::saveFile).c_str());
	} else {
		if(!C2D2M_PrimeiroBlocoMarca(mapa, C2D2M_INICIO, &xinicial, &yinicial))
		{
			// Se não achou, dá um valor default
			xinicial = 300;
			yinicial = 200;
		}
	}
	

	// Cria a personagem
	hero->iniciar(HERO, "luas\\hero.lua", &hero->AtualizaHero, xinicial, yinicial);
	
	/** CARREGA INIMIGOS **/
	
	// Calcula quantos inimigos tem no mapa
	if(C2D2M_PrimeiroBlocoMarca(mapa, MARCA_CAVEIRA, &xinicial, &yinicial))
	{
		numInimigos++;
		while(C2D2M_ProximoBlocoMarca(mapa, &xinicial, &yinicial))
			numInimigos++;
	}
	// Cria múltiplos inimigos
	inimigos = (Enemy**)malloc(sizeof(Enemy*)*numInimigos);
	memset(inimigos, 0, numInimigos*sizeof(Enemy*));
	numInimigos=0;
	// Carrega múltiplos inimigos
	if(C2D2M_PrimeiroBlocoMarca(mapa, MARCA_CAVEIRA, &xinicial, &yinicial)) {
		inimigos[numInimigos] = new Enemy(LARGURA_JANELA, ALTURA_JANELA);
		inimigos[numInimigos++]->iniciar(ENEMY, "luas\\enemy.lua", &inimigos[numInimigos]->AtualizaEnemy, xinicial, yinicial);
		while(C2D2M_ProximoBlocoMarca(mapa, &xinicial, &yinicial)) {
			inimigos[numInimigos] = new Enemy(LARGURA_JANELA, ALTURA_JANELA);
			inimigos[numInimigos++]->iniciar(ENEMY, "luas\\enemy.lua", &inimigos[numInimigos]->AtualizaEnemy, xinicial, yinicial);
		}
	}

	// Cria a mira
	mira = ATOR_CriaAtor(JOGO_MIRA, xinicial, yinicial, 0);

	// Indica que achou a saída (ou não, por default)
	bool encerrou=false;
	// O evento
	Evento ev;
	// Roda!
	//while(!teclas[C2D2_ENCERRA].ativo && !teclas[C2D2_ESC].ativo &&	hero->getEstado() != ATOR_ENCERRADO)
	bool encerra2 = false;
	while(encerra2 == false)
	{
		if(teclas[C2D2_ESC].ativo){
			encerra2 = true;
			estadoatual =1;
		}else{
			// Limpa a tela
			C2D2_LimpaTela();
			// Se não encerrou, roda a lógica
			if(!encerrou)
			{
				hero->aplicar_estado(mapa);
				// Aplicar estado para todos os inimigos
				for(int i=0;i<numInimigos;i++)
					if(inimigos[i]!=0)
						inimigos[i]->aplicar_estado(mapa);
			
				ATOR_AplicaEstado(mira, mapa, LARGURA_JANELA, ALTURA_JANELA);
				// Processa o controle da atriz
				hero->ProcessaControle();
			
				// verifica se a atriz não morre/encerrou a fase (colisão com cenário)
				if(hero->getEstado() != ATOR_ENCERRADO && hero->getEstado() != ATOR_MORTO)
				{
					if(hero->checkBlockCollision(mapa, C2D2M_MORTE))
					{
						ev.tipoEvento = EVT_COLIDIU_ARMADILHA;
						hero->enviar_evento(&ev);
					}
					// Verifica se a atriz não encontrou o bloco da saída
					if(hero->checkBlockCollision(mapa, C2D2M_FIM))
					{
						ev.tipoEvento = EVT_FIM_FASE;
						hero->enviar_evento(&ev);
						encerrou = true;
					}
					// Verifica se hero colidiu com checkpoint
					if (hero->checkBlockCollision(mapa, C2D2M_CHECK)) {
						// int v = atoi(CIniFile::GetValue("HERO_INIT_X","Main",FileName).c_str());

						char buffer [33];
						itoa (hero->getPosX(),buffer,10);
						CIniFile::SetValue("HERO_INIT_X", buffer, "Main", (string)GameHelper::saveFile);

						itoa (hero->getPosY(),buffer,10);
						CIniFile::SetValue("HERO_INIT_Y", buffer, "Main", (string)GameHelper::saveFile);

						/*ev.tipoEvento = EVT_CHECKPOINT;
						hero->enviar_evento(&ev);*/
					}
					// Checa colisão entre Hero e Enemies
					for(int i=0;i<numInimigos;i++) 
						if(inimigos[i]!=0) 
							if (ATOR_ColidiuAtores(hero->ator, inimigos[i]->ator)) 
								if (hero->damaged(1) == 0)
									ATOR_TrocaEstado(hero->ator, HERO_MORRENDO, false);

				}
			}
			// Roda a lógica da atriz
			hero->atualizar(mapa);
			// Aplicar estado para todos os inimigos
			for(int i=0;i<numInimigos;i++)
				if(inimigos[i]!=0)
					inimigos[i]->atualizar(mapa);

			ATOR_Atualiza(mira, 0);
		
			// Atualiza o mapa com a atriz
			hero->centerMap(mapa);
			int mapX = 0;
			int mapY = 0;
			C2D2M_PosicaoXY(mapa, &mapX, &mapY);
			mira->x = mapX + mouse->x - OFFX;
			mira->y = mapY + mouse->y - OFFY;
			// Desenha a tela
			// Para manter os sprites dentro da janela, devemos limitar a área de desenho
			// No caso de um jogo que ocupe a tela toda, podemos ignorar este passo
			C2D2_LimitaAreaDesenho(OFFX, OFFY, LARGURA_JANELA, ALTURA_JANELA);

			C2D2M_DesenhaCamadaMapa(mapa, 0, OFFX, OFFY, LARGURA_JANELA, ALTURA_JANELA);

			// Desenha os atores		
			/* if (hero->getPosY() >= enemy->getPosY()) {
				enemy->desenhar(mapa, OFFX, OFFY);
				hero->desenhar(mapa, OFFX, OFFY);
			} else 	{		
				hero->desenhar(mapa, OFFX, OFFY);
				enemy->desenhar(mapa, OFFX, OFFY);
			}*/
			hero->desenhar(mapa, OFFX, OFFY);
			for(int i=0;i<numInimigos;i++)
				if(inimigos[i]!=0)
					inimigos[i]->desenhar(mapa, OFFX, OFFY);

			ATOR_Desenha(mira, mapa, OFFX, OFFY);

			// Quando acaba de desenhar os atores, libera o redesenho na tela inteira
			C2D2_LiberaAreaDesenho();
		
			int heroScreenX = hero->getScreenPosX(mapa);
			int heroScreenY = hero->getScreenPosY(mapa);
			C2D2P_Linha(heroScreenX, heroScreenY, mouse->x, mouse->y, 215, 0, 0);

			char str[50];
			sprintf(str, "HeroScreenX: %d, HeroScreenY: %d", heroScreenX, heroScreenY);
			C2D2_DesenhaTexto(arial32, 0, ALTURA_TELA - 30, str, C2D2_TEXTO_ESQUERDA);

			// Se mira colidir com sólido
			if(ATOR_ColidiuBlocoCenario(mira, mapa, C2D2M_SOLIDO)) {
			
			}

			if (mouse->botoes[C2D2_MESQUERDO].pressionado) {
				if(ATOR_ColidiuBlocoCenario(mira, mapa, C2D2M_SOLIDO)) {
					int mapX = 0;
					int mapY = 0;
					C2D2M_PosicaoXY(mapa, &mapX, &mapY);
					int blockX = (mapX + mouse->x - OFFX)/32;
					int blockY = (mapY + mouse->y - OFFY)/32;
				
					C2D2M_AlteraBloco(mapa, 0, blockX, blockY, -70);
					C2D2M_AlteraBloco(mapa, 1, blockX, blockY, 0);
				}

				else if(ATOR_ColidiuBlocoCenario(mira, mapa, 0)) {
					int mapX = 0;
					int mapY = 0;
					C2D2M_PosicaoXY(mapa, &mapX, &mapY);
					int blockX = (mapX + mouse->x - OFFX)/32;
					int blockY = (mapY + mouse->y - OFFY)/32;
					C2D2M_AlteraBloco(mapa, 0, blockX, blockY, -38);
					C2D2M_AlteraBloco(mapa, 1, blockX, blockY, C2D2M_SOLIDO);
				}

				for(int i=0;i<numInimigos;i++)
					if(inimigos[i]!=0)
						inimigos[i]->shooted(mira);
			}

			hero->nameUpdate(mapa);
			for(int i=0;i<numInimigos;i++)
				if(inimigos[i]!=0)
					inimigos[i]->nameUpdate(mapa);

			// Se encerrou, mostra a mensagem de vitória
			if(encerrou)
				C2D2_DesenhaTexto(arial32, LARGURA_TELA/2, ALTURA_TELA/2, "Você venceu! Parabéns!!",C2D2_TEXTO_CENTRALIZADO);
			else if(hero->getEstado() == HERO_MORRENDO)
				C2D2_DesenhaTexto(arial32, LARGURA_TELA/2, ALTURA_TELA/2, "Game Over",C2D2_TEXTO_CENTRALIZADO);

			// Troca os buffers de imagem e esperar o tempo para 60fps
			C2D2_Sincroniza(C2D2_FPS_PADRAO);
			// Anima o mapa
			C2D2M_AnimaMapa(mapa);			
		}
	}
}

bool carregaRecursos(){
	// Carrega a fonte tamanho 32. Para cada recurso carregado, testamos se ele foi carregado corretamente
    fonte32 = C2D2_CarregaFonte("./graficos/arial32.png", 32);
    if(fonte32==0)
    {
        printf("Falhou carregar a fonte de tamanho 32.\n");
        return false;
    }
    // Como jб tem uma fonte carregada, pode fazer a tela de load
    C2D2_TrocaCorLimpezaTela(0,0,255);
    C2D2_LimpaTela();
    C2D2_DesenhaTexto(arial32, 400, 284, "Carregando. Por favor, aguarde.", C2D2_TEXTO_CENTRALIZADO);
    C2D2_Sincroniza(C2D2_FPS_PADRAO);
    // Continua carregando os recursos
    fonte64 = C2D2_CarregaFonte("./graficos/isabelle64.png", 64);
    if(fonte64==0)
    {
        printf("Falhou carregar a fonte de tamanho 64.\n");
        return false;
    }
    botao_jogar = C2D2_CarregaSpriteSet("./graficos/botao_jogar.png", 200, 40);
    if(botao_jogar==0)
    {
        printf("Falhou carregar o botгo de jogar.\n");
        return false;
    }
    mcredito = C2D2_CarregaSpriteSet("./graficos/mcredito.png", 0, 0);
    if(mcredito==0)
    {
        printf("Falhou carregar o botгo de creditos.\n");
        return false;
    }
    botao_creditos = C2D2_CarregaSpriteSet("./graficos/botao_creditos.png", 200, 40);
    if(botao_creditos==0)
    {
        printf("Falhou carregar o botгo de crйditos.\n");
        return false;
    }
    botao_sair = C2D2_CarregaSpriteSet("./graficos/botao_sair.png", 200, 40);
    if(botao_sair==0)
    {
        printf("Falhou carregar o botгo desair.\n");
        return false;
    }
    botao_load = C2D2_CarregaSpriteSet("./graficos/botao_load.png", 200, 40);
    if(botao_load==0)
    {
        printf("Falhou carregar o botгo de load.\n");
        return false;
    }
    botao_voltar = C2D2_CarregaSpriteSet("./graficos/botao_voltar.png", 200, 40);
    if(botao_voltar==0)
    {
        printf("Falhou carregar o botгo de voltar.\n");
        return false;
    }
    titulo = C2D2_CarregaSpriteSet("./graficos/titulo.png", 0, 0);
    if(titulo==0)
    {
        printf("Falhou carregar a tela de tнtulo.\n");
        return false;
    }
    spmira = C2D2_CarregaSpriteSet("./graficos/mira.png", 21, 21);
    if(spmira==0)
    {
        printf("Falhou carregar o sprite da mira.\n");
        return false;
    }
     
    // Se chegou até aqui, carregou tudo 100%
    return true;
}

void descarregaRecursos(){
	// Apaga a atriz
	free(hero);
	for(int i=0;i<numInimigos;i++)
		if(inimigos[i]!=0)
			free(inimigos[i]);
	free(mira);
	// DEscarrega o modelo da atriz
	ATOR_DescarregaAtor(HERO);
	ATOR_DescarregaAtor(ENEMY);
	ATOR_DescarregaAtor(JOGO_MIRA);
	// Apaga as imagens carregadas na memória
	C2D2_RemoveFonte(arial32);
	C2D2_RemoveFonte(fonte32);
    C2D2_RemoveFonte(fonte64);
    C2D2_RemoveSpriteSet(botao_jogar);
	C2D2_RemoveSpriteSet(botao_load);
    C2D2_RemoveSpriteSet(botao_creditos);
    C2D2_RemoveSpriteSet(botao_sair);
    C2D2_RemoveSpriteSet(botao_voltar);
    C2D2_RemoveSpriteSet(spmira);
}

void menu(){
	// Recupera o teclado
	C2D2_Botao *teclas = C2D2_PegaTeclas();
    C2D2_Mouse *mouse = C2D2_PegaMouse();

    // Variáveis para os botões, 1 se o mouse está em cima, 0 caso contrário
    int bjogar=0, bcreditos=0, bsair=0, bload=0;

    // Sincroniza para atualizar o teclado
	C2D2_Sincroniza(C2D2_FPS_PADRAO);
	do
	{
        // Lógica: se pressiona ESC no menu, deve sair fora
        if(teclas[C2D2_ESC].pressionado)
            encerra=true;
        // Vê se o mouse está em cima de um botão e atualiza o estado
        if(C2D2_ColidiuSprites(spmira, 0, mouse->x, mouse->y, botao_jogar, 0, 200,200))
            bjogar=1;
        else
            bjogar=0;
        if(C2D2_ColidiuSprites(spmira, 0, mouse->x, mouse->y, botao_creditos, 0, 200,250))
            bcreditos=1;
        else
            bcreditos=0;
        if(C2D2_ColidiuSprites(spmira, 0, mouse->x, mouse->y, botao_sair, 0, 200,350))
            bsair=1;
        else
            bsair=0;
		if(C2D2_ColidiuSprites(spmira, 0, mouse->x, mouse->y, botao_load, 0, 200,300))
			bload=1;
		else
			bload=0;
        // Se o botão do mouse está pressionado, toma uma ação
        if(mouse->botoes[C2D2_MESQUERDO].pressionado)
        {
            // O botão ativo é o botão da ação equivalente
            if(bjogar==1)
                estadoatual = 2;
            else if(bcreditos==1)
                estadoatual=3;
			else if(bload==1)
				estadoatual =4;
            else if(bsair==1)
                encerra=true;
        }

        // Limpa a tela e desenha
		C2D2_LimpaTela();
        // Desenha a tela de título
        C2D2_DesenhaSprite(titulo, 0, 0, 0);
        // Desenha os botões de acordo com o estado
        C2D2_DesenhaSprite(botao_jogar, bjogar, 200,200);
        C2D2_DesenhaSprite(botao_creditos, bcreditos, 200,250);
        C2D2_DesenhaSprite(botao_sair, bsair, 200,350);
		C2D2_DesenhaSprite(botao_load, bload, 200,300);
        //Desenha a mira
        C2D2_DesenhaSprite(spmira, 0, mouse->x, mouse->y);
		// Faz a troca do front buffer com o back buffer e atualiza as entradas
		C2D2_Sincroniza(C2D2_FPS_PADRAO);
	}
	while(!encerra && estadoatual==1);
}

void creditos(){
	 // Recupera o teclado
    C2D2_Botao *teclas = C2D2_PegaTeclas();
    C2D2_Mouse *mouse = C2D2_PegaMouse();
     
    // Variбvel para o botгo, 1 se o mouse estб em cima, 0 caso contrбrio
    int bvoltar=0;
 
    // Sincroniza para atualizar o teclado
    C2D2_Sincroniza(C2D2_FPS_PADRAO);
    // Troca a cor de limpeza para verde
    C2D2_TrocaCorLimpezaTela(0, 0, 0);
     
    do
    {
        // Lуgica: se pressiona ESC no menu, deve sair fora
        if(teclas[C2D2_ESC].pressionado)
            estadoatual=1;
        // Vк se o mouse estб em cima de um botгo e atualiza o estado
        if(C2D2_ColidiuSprites(spmira, 0, mouse->x, mouse->y, botao_voltar, 0, 350, 400))
            bvoltar=1;
        else
            bvoltar=0;
        // Se o botгo do mouse estб pressionado, toma uma aзгo
        if(mouse->botoes[C2D2_MESQUERDO].pressionado)
        {
            // O botгo ativo й o botгo da aзгo equivalente
            if(bvoltar==1)
                estadoatual=1;
        }
 
        // Limpa a tela e desenha
        C2D2_LimpaTela();
        C2D2_DesenhaSprite(mcredito, 0, 0, 0);
        // Desenha o botгo de acordo com o estado
        C2D2_DesenhaSprite(botao_voltar, bvoltar,350 ,400);
        //Desenha a mira
        C2D2_DesenhaSprite(spmira, 0, mouse->x, mouse->y);
        // Faz a troca do front buffer com o back buffer e atualiza as entradas
        C2D2_Sincroniza(C2D2_FPS_PADRAO);
    }
    while(estadoatual==3);
}

int main(int narg, char **valarg)
{
	CL2_Inicia();
	// Inicia a Chien2D 2 e testa se deu tudo certo
	if(C2D2_Inicia())
		printf("Iniciou a Chien2D 2 com sucesso\n");
	else
	{
		printf("Falhou ao iniciar a Chien2D 2. Encerrando o programa.\n");
		return 0;
	}
	// Inicia as primitivas
	C2D2P_Inicia();
	// Inicia os mapas
	C2D2M_Inicia();
	// Inicia os personagens
	ATOR_Inicia();
	// Ativa mouse
	
	// Carrega o mapa
	mapa = C2D2M_CarregaMapaMappy("jogo.FMP","img\\TileSetjogo.bmp");
	// bool cAtriz = JOGO_CarregaAtriz();
	hero = new Hero(LARGURA_JANELA, ALTURA_JANELA);
	// Carrega os recursos e testa se pode continuar (se carregou tudo certo)
    if(carregaRecursos())
    {
        do
	    {
            switch(estadoatual)
            {
                case 1:
                    menu();
                    break;
                case 2:
                    telaJogo();
                    break;
                case 3:
					creditos();
                    break;
				case 4:
					load = true;
					telaJogo();
					break;
                default:
                    // Deu erro, encerra
                    encerra=true;
                    break;
            }
	    }
	    while(!encerra);
    }
	descarregaRecursos();
	// Encerra os mapas
	C2D2M_Encerra();
	// Encerra a Chien2D 2
	C2D2_Encerra();
	CL2_Encerra();

	return 0;
}
